#ifndef __M_UTIL_H__
#define __M_UTIL_H__
#include<iostream>
#include<sstream>
#include<cassert>
#include<mutex>
#include<memory>
#include<mysql/mysql.h>
#include<string>
#include<jsoncpp/json/json.h>

namespace im{
  //Json数据类封装
  class JsonUntil
  {
    public:
      //序列化
      static bool serialize(Json::Value& value,std::string* body)
      {
        Json::StreamWriterBuilder swb;
        std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());
        std::stringstream ss;
        int ret = sw->write(value,&ss);
        if(ret!=0)
        {
          std::cout<<"serial ize failed"<<std::endl;
          return false;
        }
        *body = ss.str();
        return true;
      }

      //反序列化
      static bool unserialize(std::string& body,Json::Value* val)
      {
        Json::CharReaderBuilder crb;
        std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
        std::string err;
        bool ret = cr->parse(body.c_str(),body.c_str()+body.size(),val,&err);
        if(ret==false)
        {
          std::cout<<"unserialize failed\n";
          return false;
        }
        return true;
      }
  };

  //数据库类封装
  class MysqlUtil
  {
    public:
      static MYSQL* mysql_create(const std::string& host,const std::string& user,
      const std::string& pass,const std::string& db,int port)
      {
        MYSQL* mysql = mysql_init(NULL);
        if(mysql==NULL)
        {
          std::cout<<"mysql init failed\n";
          return nullptr;
        }
        //连接数据库
       if(mysql_real_connect(mysql,host.c_str(),user.c_str(),pass.c_str(),db.c_str(),port,NULL,0)==NULL)
       {
         std::cout<<"connect failed: "<<mysql_error(mysql)<<std::endl;
         mysql_close(mysql);
         return NULL;
       }
       //设置字符集
       mysql_set_character_set(mysql,"utf8");
       return mysql;
      }
      
      //执行语句
      static bool mysql_exec(MYSQL* mysql,const std::string& sql)
      {
        int ret = mysql_query(mysql,sql.c_str());
        if(ret!=0)
        {
          std::cout<<sql<<std::endl;
          std::cout<<"query failed: "<<mysql_error(mysql)<<std::endl;
          return false;
        }
        return true;
      }
      //关闭句柄
      static void  mysql_destroy(MYSQL* mysql)
      {
        if(mysql==NULL)
        {
          return;
        }
        mysql_close(mysql);
      }
  };

  //用户信息表封装
  class UserTable
  {
    private:
      MYSQL* _mysql;
      std::mutex _mutex;
    public:
      //初始化数据库操作句柄
      UserTable(const std::string& host,const std::string& user,
      const std::string& pass,const std::string& db,int port)
      {
        _mysql = MysqlUtil::mysql_create(host,user,pass,db,port);
        assert(_mysql!=NULL);
      }
      //释放句柄
      ~UserTable()
      {
        MysqlUtil::mysql_destroy(_mysql);
      }
      //新增用户
      bool Insert(const Json::Value& user)
      {
        //用户ID，用户名，密码，昵称，创建时间
#define INSERT_USER "insert user values(null,'%s','%s',MD5('%s'),now());"
        char sql[4096]={0};
        snprintf(sql,4095,INSERT_USER,user["username"].asCString(),
           user["nickname"].asCString(),
           user["password"].asCString());
        return MysqlUtil::mysql_exec(_mysql,sql);
      }
      //登录验证
      bool check_login(const Json::Value& user)
      {
#define LOGIN_CHECK "select id from user where username='%s' and password=MD5('%s');"
        char sql[4096]={0};
        snprintf(sql,4095,LOGIN_CHECK,
            user["username"].asCString(),
            user["password"].asCString());
        MYSQL_RES* res;
        {
            //加锁
            std::unique_lock<std::mutex> lock(_mutex);
            bool ret =MysqlUtil::mysql_exec(_mysql,sql);
            if(ret==false)
            {
              return false;
            }
            //将结果集保存早本地
            res = mysql_store_result(_mysql);
            if(res == NULL)
            {
              std::cout<<"store result failed:"<<mysql_error(_mysql)<<std::endl;
            }
          }
          //取出结果集中的内容
          int num_row = mysql_num_rows(res);
          if(num_row!=1)
          {
            mysql_free_result(res);
            return false;
          }
          mysql_free_result(res);
          return true;
      }
      //修改密码
      bool update_password(const Json::Value& user)
      {
#define MO_PASSWD_BY_NAME "update user set password=MD5('%s') where username='%s' and password=MD5('%s');"
        char sql[4096]={0};                      
        snprintf(sql,4095,MO_PASSWD_BY_NAME,
								 user["new_password"].asCString(),
								 user["username"].asCString(),
								 user["old_password"].asCString());  
				return MysqlUtil::mysql_exec(_mysql,sql);
      }
      //修改昵称
      bool update_nickname(const Json::Value& user)
			{
#define MO_NICKNAME_BY_NAME "update user set nickname='%s' where username='%s' and password=MD5('%s');"
				 char sql[4096]={0};                                                                                
         snprintf(sql,4095,MO_NICKNAME_BY_NAME,
         user["nickname"].asCString(),                    
         user["username"].asCString(),    
         user["password"].asCString());
				 return MysqlUtil::mysql_exec(_mysql,sql);   
			}
      //删除用户
      bool remove(int id)
			{
#define DELETE_USER "delete from user where id=%d;"
        char sql[4096]={0};
        snprintf(sql,4095,DELETE_USER,id);
        return MysqlUtil::mysql_exec(_mysql,sql);
			}

      //通过id来获取用户信息
      bool select_by_id(int id,Json::Value* user)
      {
#define SELECT_USER_BY_ID "select username,nickname from user where id=%d;"
        char sql[4096]={0};
        snprintf(sql,4095,SELECT_USER_BY_ID,id);
        MYSQL_RES* res;
        {
          //加锁
          std::unique_lock<std::mutex> lock(_mutex);
          bool ret =MysqlUtil::mysql_exec(_mysql,sql);
          if(ret==false)
          {
            return false;
          }
          res = mysql_store_result(_mysql);
          if(res == NULL)
          {
            std::cout<<"store result failed:"<<mysql_error(_mysql)<<std::endl;
          }
        }
        //将结果集保存早本地
        int num_row = mysql_num_rows(res);
        if(num_row!=1)
        {
          std::cout<<"query result error!\n";
          return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        (*user)["id"] = id;
        (*user)["username"] = row[0];
        (*user)["nickname"] = row[1];
        //释放结果集
        mysql_free_result(res);
        return true;
      }
      //通过名称来获取用户信息
      bool select_by_name(const std::string& name,Json::Value* user)
      {
        #define SELECT_USER_BY_NAME "select id,nickname from user where username='%s';"
        char sql[4096]={0};
        snprintf(sql,4095,SELECT_USER_BY_NAME,name.c_str());
        MYSQL_RES* res;
        {
          //加锁
          std::unique_lock<std::mutex> lock(_mutex);
          bool ret =MysqlUtil::mysql_exec(_mysql,sql);
          if(ret==false)
          {
            return false;
          }
          res = mysql_store_result(_mysql);
          if(res == NULL)
          {
            std::cout<<"store result failed:"<<mysql_error(_mysql)<<std::endl;
          }
        }
        //将结果集保存早本地
        int num_row = mysql_num_rows(res);
        if(num_row!=1)
        {
          std::cout<<"query result error!\n";
          return false;
        }
        MYSQL_ROW row = mysql_fetch_row(res);
        (*user)["id"] = std::stoi(row[0]);
        (*user)["username"] = name;
        (*user)["nickname"] = row[1];
        //释放结果集
        mysql_free_result(res);
        return true;
      }
  };

  //发送消息表封装
  class MsgTable
  {
    private:
      MYSQL* _mysql;
      std::mutex _mutex;
    public:
      MsgTable(const std::string& host,const std::string& user,
      const std::string& pass,const std::string& db,int port)
      {
        _mysql = MysqlUtil::mysql_create(host,user,pass,db,port);
        assert(_mysql!=NULL);
      }
      ~MsgTable()
      {
        MysqlUtil::mysql_destroy(_mysql);
      }
      bool insert(const Json::Value& msg)
      {
#define INSERT_MSG "insert `message` values(null,%d,'%s','%s',now());"
        char sql[4096]={0};
        snprintf(sql,4095,INSERT_MSG,msg["uid"].asInt(),msg["nickname"].asCString(),msg["msg"].asCString());
        return MysqlUtil::mysql_exec(_mysql,sql);
      }
      bool select_part(int sec,Json::Value* msg)
      {
#define SELECT_PART_MSG "select* from message where timestampdiff(second,ctime,now())<%d;"
        char sql[4096]={0};
        snprintf(sql,4095,SELECT_PART_MSG,sec);
        MYSQL_RES* res;
        {
          //加锁
          std::unique_lock<std::mutex> lock(_mutex);
          //执行语句
          bool ret =MysqlUtil::mysql_exec(_mysql,sql);
          if(ret==false)
          {
            return false;
          }
          //保存结果到本地
          res = mysql_store_result(_mysql);
          if(res == NULL)
          {
            std::cout<<"store result failed:"<<mysql_error(_mysql)<<std::endl;
          }
        }
        int num_row = mysql_num_rows(res);
        
        for(int i=0;i<num_row;++i)
        {
          MYSQL_ROW row = mysql_fetch_row(res);
          Json::Value m;
          m["id"]=std::stoi(row[0]);
          m["uid"]=std::stoi(row[1]);
          m["nickname"]=row[2];
          m["msg"]=row[3];
          m["ctime"]=row[4];
          msg->append(m);
        }
        mysql_free_result(res);
        return true;
      }
  };
}

#endif